﻿using huqiang.Font;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(CanvasRenderer))]
[AddComponentMenu("UI/NText", 100)]
/// <summary>
/// The default Graphic to draw font data to screen.
/// </summary>
public class NText : MaskableGraphic, ILayoutElement
{
    [SerializeField] private FontData m_FontData = FontData.defaultFontData;

    [SerializeField] private BaseFont m_Font;
#if UNITY_EDITOR
    // needed to track font changes from the inspector
    private BaseFont m_LastTrackedFont;
#endif

    [TextArea(3, 10)] [SerializeField] protected string m_Text = String.Empty;

    private TextGenerator m_TextCache;
    private TextGenerator m_TextCacheForLayout;

    static protected Material s_DefaultText = null;

    // We use this flag instead of Unregistering/Registering the callback to avoid allocation.
    [NonSerialized] protected bool m_DisableFontTextureRebuiltCallback = false;

    protected NText()
    {
        useLegacyMeshGeneration = false;
    }

    /// <summary>
    /// The cached TextGenerator used when generating visible Text.
    /// </summary>

    public TextGenerator cachedTextGenerator
    {
        get { return m_TextCache ?? (m_TextCache = (m_Text.Length != 0 ? new TextGenerator(m_Text.Length) : new TextGenerator())); }
    }

    /// <summary>
    /// The cached TextGenerator used when determine Layout
    /// </summary>
    public TextGenerator cachedTextGeneratorForLayout
    {
        get { return m_TextCacheForLayout ?? (m_TextCacheForLayout = new TextGenerator()); }
    }
    [SerializeField] bool m_emojText;

    /// <summary>
    /// Text's texture comes from the font.
    /// </summary>
    public override Texture mainTexture
    {
        get
        {
            //if (font != null && font.material != null && font.material.mainTexture != null)
            //    return font.material.mainTexture;

            //if (m_Material != null)
            //    return m_Material.mainTexture;

            return base.mainTexture;
        }
    }

    /// <summary>
    /// Called by the FontUpdateTracker when the texture associated with a font is modified.
    /// </summary>
    public void FontTextureChanged()
    {
        // Only invoke if we are not destroyed.
        if (!this)
            return;

        if (m_DisableFontTextureRebuiltCallback)
            return;

        cachedTextGenerator.Invalidate();

        if (!IsActive())
            return;

        // this is a bit hacky, but it is currently the
        // cleanest solution....
        // if we detect the font texture has changed and are in a rebuild loop
        // we just regenerate the verts for the new UV's
        if (CanvasUpdateRegistry.IsRebuildingGraphics() || CanvasUpdateRegistry.IsRebuildingLayout())
            UpdateGeometry();
        else
        {
            SetAllDirty();
        }
    }

    /// <summary>
    /// The Font used by the text.
    /// </summary>
    /// <remarks>
    /// This is the font used by the Text component. Use it to alter or return the font from the Text. There are many free fonts available online.
    /// </remarks>
    /// <example>
    /// <code>
    /// <![CDATA[
    /// //Create a new Text GameObject by going to Create>UI>Text in the Editor. Attach this script to the Text GameObject. Then, choose or click and drag your own font into the Font section in the Inspector window.
    ///
    /// using UnityEngine;
    /// using UnityEngine.UI;
    ///
    /// public class TextFontExample : MonoBehaviour
    /// {
    ///     Text m_Text;
    ///     //Attach your own Font in the Inspector
    ///     public Font m_Font;
    ///
    ///     void Start()
    ///     {
    ///         //Fetch the Text component from the GameObject
    ///         m_Text = GetComponent<Text>();
    ///     }
    ///
    ///     void Update()
    ///     {
    ///         if (Input.GetKey(KeyCode.Space))
    ///         {
    ///             //Change the Text Font to the Font attached in the Inspector
    ///             m_Text.font = m_Font;
    ///             //Change the Text to the message below
    ///             m_Text.text = "My Font Changed!";
    ///         }
    ///     }
    /// }
    /// ]]>
    ///</code>
    /// </example>
    public BaseFont font
    {
        get
        {
            return m_Font;
        }
        set
        {
            if (m_FontData.font == value)
                return;

            //if (isActiveAndEnabled)
            //    FontUpdateTracker.UntrackText(this);

            m_Font = value;

            //if (isActiveAndEnabled)
            //    FontUpdateTracker.TrackText(this);

#if UNITY_EDITOR
            // needed to track font changes from the inspector
            m_LastTrackedFont = value;
#endif

            SetAllDirty();
        }
    }

    /// <summary>
    /// Text that's being displayed by the Text.
    /// </summary>
    /// <remarks>
    /// This is the string value of a Text component. Use this to read or edit the message displayed in Text.
    /// </remarks>
    /// <example>
    /// <code>
    /// <![CDATA[
    /// using UnityEngine;
    /// using UnityEngine.UI;
    ///
    /// public class Example : MonoBehaviour
    /// {
    ///     public Text m_MyText;
    ///
    ///     void Start()
    ///     {
    ///         //Text sets your text to say this message
    ///         m_MyText.text = "This is my text";
    ///     }
    ///
    ///     void Update()
    ///     {
    ///         //Press the space key to change the Text message
    ///         if (Input.GetKey(KeyCode.Space))
    ///         {
    ///             m_MyText.text = "My text has now changed.";
    ///         }
    ///     }
    /// }
    /// ]]>
    ///</code>
    /// </example>
    public virtual string text
    {
        get
        {
            return m_Text;
        }
        set
        {
            if (String.IsNullOrEmpty(value))
            {
                if (String.IsNullOrEmpty(m_Text))
                    return;
                m_Text = "";
                SetVerticesDirty();
            }
            else if (m_Text != value)
            {
                m_Text = value;
                SetVerticesDirty();
                SetLayoutDirty();
            }
        }
    }

    /// <summary>
    /// Whether this Text will support rich text.
    /// </summary>

    public bool supportRichText
    {
        get
        {
            return m_FontData.richText;
        }
        set
        {
            if (m_FontData.richText == value)
                return;
            m_FontData.richText = value;
            SetVerticesDirty();
            SetLayoutDirty();
        }
    }

    /// <summary>
    /// Should the text be allowed to auto resized.
    /// </summary>

    public bool resizeTextForBestFit
    {
        get
        {
            return m_FontData.bestFit;
        }
        set
        {
            if (m_FontData.bestFit == value)
                return;
            m_FontData.bestFit = value;
            SetVerticesDirty();
            SetLayoutDirty();
        }
    }

    /// <summary>
    /// The minimum size the text is allowed to be.
    /// </summary>
    public int resizeTextMinSize
    {
        get
        {
            return m_FontData.minSize;
        }
        set
        {
            if (m_FontData.minSize == value)
                return;
            m_FontData.minSize = value;

            SetVerticesDirty();
            SetLayoutDirty();
        }
    }

    /// <summary>
    /// The maximum size the text is allowed to be. 1 = infinitely large.
    /// </summary>
    public int resizeTextMaxSize
    {
        get
        {
            return m_FontData.maxSize;
        }
        set
        {
            if (m_FontData.maxSize == value)
                return;
            m_FontData.maxSize = value;

            SetVerticesDirty();
            SetLayoutDirty();
        }
    }

    public TextAnchor alignment
    {
        get
        {
            return m_FontData.alignment;
        }
        set
        {
            if (m_FontData.alignment == value)
                return;
            m_FontData.alignment = value;

            SetVerticesDirty();
            SetLayoutDirty();
        }
    }

    /// <summary>
    /// Use the extents of glyph geometry to perform horizontal alignment rather than glyph metrics.
    /// </summary>
    /// <remarks>
    /// This can result in better fitting left and right alignment, but may result in incorrect positioning when attempting to overlay multiple fonts (such as a specialized outline font) on top of each other.
    /// </remarks>
    public bool alignByGeometry
    {
        get
        {
            return m_FontData.alignByGeometry;
        }
        set
        {
            if (m_FontData.alignByGeometry == value)
                return;
            m_FontData.alignByGeometry = value;

            SetVerticesDirty();
        }
    }

    public int fontSize
    {
        get
        {
            return m_FontData.fontSize;
        }
        set
        {
            if (m_FontData.fontSize == value)
                return;
            m_FontData.fontSize = value;

            SetVerticesDirty();
            SetLayoutDirty();
        }
    }

    /// <summary>
    /// Horizontal overflow mode.
    /// </summary>
    /// <remarks>
    /// When set to HorizontalWrapMode.Overflow, text can exceed the horizontal boundaries of the Text graphic. When set to HorizontalWrapMode.Wrap, text will be word-wrapped to fit within the boundaries.
    /// </remarks>
    public HorizontalWrapMode horizontalOverflow
    {
        get
        {
            return m_FontData.horizontalOverflow;
        }
        set
        {
            if (m_FontData.horizontalOverflow == value)
                return;
            m_FontData.horizontalOverflow = value;

            SetVerticesDirty();
            SetLayoutDirty();
        }
    }

    /// <summary>
    /// Vertical overflow mode.
    /// </summary>
    public VerticalWrapMode verticalOverflow
    {
        get
        {
            return m_FontData.verticalOverflow;
        }
        set
        {
            if (m_FontData.verticalOverflow == value)
                return;
            m_FontData.verticalOverflow = value;

            SetVerticesDirty();
            SetLayoutDirty();
        }
    }

    /// <summary>
    /// Line spacing, specified as a factor of font line height. A value of 1 will produce normal line spacing.
    /// </summary>
    public float lineSpacing
    {
        get
        {
            return m_FontData.lineSpacing;
        }
        set
        {
            if (m_FontData.lineSpacing == value)
                return;
            m_FontData.lineSpacing = value;

            SetVerticesDirty();
            SetLayoutDirty();
        }
    }

    /// <summary>
    /// Font style used by the Text's text.
    /// </summary>

    public FontStyle fontStyle
    {
        get
        {
            return m_FontData.fontStyle;
        }
        set
        {
            if (m_FontData.fontStyle == value)
                return;
            m_FontData.fontStyle = value;

            SetVerticesDirty();
            SetLayoutDirty();
        }
    }

    /// <summary>
    /// Provides information about how fonts are scale to the screen.
    /// </summary>
    /// <remarks>
    /// For dynamic fonts, the value is equivalent to the scale factor of the canvas. For non-dynamic fonts, the value is calculated from the requested text size and the size from the font.
    /// </remarks>
    public float pixelsPerUnit
    {
        get
        {
            var localCanvas = canvas;
            if (!localCanvas)
                return 1;
            // For dynamic fonts, ensure we use one pixel per pixel on the screen.
            if (m_Font is DynamicFont)
                return localCanvas.scaleFactor;
            // For non-dynamic fonts, calculate pixels per unit based on specified font size relative to font object's own font size.
            if (m_FontData.fontSize <= 0 || font.fontSize <= 0)
                return 1;
            return font.fontSize / (float)m_FontData.fontSize;
        }
    }

    protected override void OnEnable()
    {
        base.OnEnable();
        cachedTextGenerator.Invalidate();
        //FontUpdateTracker.TrackText(this);
    }

    protected override void OnDisable()
    {
        //FontUpdateTracker.UntrackText(this);
        base.OnDisable();
    }

    protected override void UpdateGeometry()
    {
        if (font != null)
        {
            base.UpdateGeometry();
        }
    }

#if UNITY_EDITOR
    protected override void Reset()
    {
        AssignDefaultFontIfNecessary();
    }

#endif
    internal void AssignDefaultFont()
    {
        //font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
    }

    internal void AssignDefaultFontIfNecessary()
    {
        //if (font == null)
        //    font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
    }

    /// <summary>
    /// Convenience function to populate the generation setting for the text.
    /// </summary>
    /// <param name="extents">The extents the text can draw in.</param>
    /// <returns>Generated settings.</returns>
    public TextGenerationSettings GetGenerationSettings(Vector2 extents)
    {
        var settings = new TextGenerationSettings();

        settings.generationExtents = extents;
        if (font != null && font is DynamicFont)
        {
            settings.fontSize = m_FontData.fontSize;
            settings.resizeTextMinSize = m_FontData.minSize;
            settings.resizeTextMaxSize = m_FontData.maxSize;
        }

        // Other settings
        settings.textAnchor = m_FontData.alignment;
        settings.alignByGeometry = m_FontData.alignByGeometry;
        settings.scaleFactor = pixelsPerUnit;
        settings.color = color;
        settings.font = null;
        settings.pivot = rectTransform.pivot;
        settings.richText = m_FontData.richText;
        settings.lineSpacing = m_FontData.lineSpacing;
        settings.fontStyle = m_FontData.fontStyle;
        settings.resizeTextForBestFit = m_FontData.bestFit;
        settings.updateBounds = false;
        settings.horizontalOverflow = m_FontData.horizontalOverflow;
        settings.verticalOverflow = m_FontData.verticalOverflow;

        return settings;
    }

    /// <summary>
    /// Convenience function to determine the vector offset of the anchor.
    /// </summary>
    static public Vector2 GetTextAnchorPivot(TextAnchor anchor)
    {
        switch (anchor)
        {
            case TextAnchor.LowerLeft: return new Vector2(0, 0);
            case TextAnchor.LowerCenter: return new Vector2(0.5f, 0);
            case TextAnchor.LowerRight: return new Vector2(1, 0);
            case TextAnchor.MiddleLeft: return new Vector2(0, 0.5f);
            case TextAnchor.MiddleCenter: return new Vector2(0.5f, 0.5f);
            case TextAnchor.MiddleRight: return new Vector2(1, 0.5f);
            case TextAnchor.UpperLeft: return new Vector2(0, 1);
            case TextAnchor.UpperCenter: return new Vector2(0.5f, 1);
            case TextAnchor.UpperRight: return new Vector2(1, 1);
            default: return Vector2.zero;
        }
    }

    readonly UIVertex[] m_TempVerts = new UIVertex[4];
    protected override void OnPopulateMesh(VertexHelper toFill)
    {
        if (font == null)
            return;
        
        // We don't care if we the font Texture changes while we are doing our Update.
        // The end result of cachedTextGenerator will be valid for this instance.
        // Otherwise we can get issues like Case 619238.
        m_DisableFontTextureRebuiltCallback = true;

        Vector2 extents = rectTransform.rect.size;

        GetGenerationSettings(ref extents, ref settingsex);
        huqiang.Font.TextMesh.Reset(0,0, m_FontData.fontSize);
        //huqiang.Font.TextMesh.customFont = customFont;

        if (m_FontData.richText)
        {
            //huqiang.Font.TextMesh.LayoutRichString(richString, ref settingsex);
        }
        else if (m_emojText)
        {
            huqiang.Font.TextMesh.Reset(0, 0, m_FontData.fontSize);
            huqiang.Font.TextMesh.LayoutUniString(m_Text, ref settingsex);
        }
        else
            huqiang.Font.TextMesh.LayoutString(m_Text, ref settingsex);

        huqiang.Font.TextMesh.LayoutOver();
        huqiang.Font.TextMesh.CreateVertex(toFill);
        material.SetTexture("_t1", m_Font.MainTexture);
        material.SetTexture("_t2", huqiang.Font.TextMesh._emojiFont.MainTexture);
        //material.SetTexture("_t2", huqiang.Font.TextMesh._dyFont.mainTexture);
        //ApplyMesh(Renderer, huqiang.Font.TextMesh.vertices1, huqiang.Font.TextMesh.Triangle1);
        //toFill.Clear();
        //List<UIVertex> verts = new List<UIVertex>();
        //List<int> tris = new List<int>();
        //UIVertex vertex = new UIVertex();
        //for(int i=0;i< huqiang.Font.TextMesh.vertices1.Count;i++)
        //{
        //    HVertex hv = huqiang.Font.TextMesh.vertices1[i];
        //    vertex.position = hv.position;
        //    vertex.color = hv.color;
        //    vertex.uv0 = hv.uv;
        //    vertex.uv1.y = 0;
        //    verts.Add(vertex);
        //}
        //for (int i = 0; i < huqiang.Font.TextMesh.Triangle1.Count; i++)
        //    tris.Add(huqiang.Font.TextMesh.Triangle1[i]);
        //toFill.AddUIVertexStream(verts,tris);

        //toFill.AddUIVertexQuad();
        //renderer.ColorMode = 1;
        //if (Font2 != null)
        //    renderer.texture = Font2.texture;
        //if (m_emojText && huqiang.Font.TextMesh._emojiFont != null)
        //{
        //    if (sub1 == null)
        //        sub1 = new UIRenderer();
        //    ApplyMesh(sub1, huqiang.Font.TextMesh.vertices2, huqiang.Font.TextMesh.Triangle2);
        //    sub1.texture = huqiang.Font.TextMesh._emojiFont.Texture;
        //    sub1.ColorMode = 2;
        //}
        //else
        //{
        //    if (sub1 != null)
        //        sub1.mesh.vertCache.DataCount = 0;
        //}
        //if (sub2 == null)
        //    sub2 = new UIRenderer();
        //ApplyMesh(sub2, huqiang.Font.TextMesh.vertices3, huqiang.Font.TextMesh.Triangle3);
        //// We have no verts to process just return (case 1037923)
        //if (vertCount <= 0)
        //{
        //    toFill.Clear();
        //    return;
        //}

        //Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y) * unitsPerPixel;
        //roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset;
        //toFill.Clear();
        //if (roundingOffset != Vector2.zero)
        //{
        //    for (int i = 0; i < vertCount; ++i)
        //    {
        //        int tempVertsIndex = i & 3;
        //        m_TempVerts[tempVertsIndex] = verts[i];
        //        m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
        //        m_TempVerts[tempVertsIndex].position.x += roundingOffset.x;
        //        m_TempVerts[tempVertsIndex].position.y += roundingOffset.y;
        //        if (tempVertsIndex == 3)
        //            toFill.AddUIVertexQuad(m_TempVerts);
        //    }
        //}
        //else
        //{
        //    for (int i = 0; i < vertCount; ++i)
        //    {
        //        int tempVertsIndex = i & 3;
        //        m_TempVerts[tempVertsIndex] = verts[i];
        //        m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
        //        if (tempVertsIndex == 3)
        //            toFill.AddUIVertexQuad(m_TempVerts);
        //    }
        //}

        m_DisableFontTextureRebuiltCallback = false;
    }

    public virtual void CalculateLayoutInputHorizontal() { }
    public virtual void CalculateLayoutInputVertical() { }

    public virtual float minWidth
    {
        get { return 0; }
    }

    public virtual float preferredWidth
    {
        get
        {
            var settings = GetGenerationSettings(Vector2.zero);
            return cachedTextGeneratorForLayout.GetPreferredWidth(m_Text, settings) / pixelsPerUnit;
        }
    }

    public virtual float flexibleWidth { get { return -1; } }

    public virtual float minHeight
    {
        get { return 0; }
    }

    public virtual float preferredHeight
    {
        get
        {
            var settings = GetGenerationSettings(new Vector2(GetPixelAdjustedRect().size.x, 0.0f));
            return cachedTextGeneratorForLayout.GetPreferredHeight(m_Text, settings) / pixelsPerUnit;
        }
    }

    public virtual float flexibleHeight { get { return -1; } }

    public virtual int layoutPriority { get { return 0; } }

#if UNITY_EDITOR
    public override void OnRebuildRequested()
    {
        // After a Font asset gets re-imported the managed side gets deleted and recreated,
        // that means the delegates are not persisted.
        // so we need to properly enforce a consistent state here.
        if (isActiveAndEnabled)
        {
            //FontUpdateTracker.UntrackText(this);
            //FontUpdateTracker.TrackText(this);
        }

        // Also the textgenerator is no longer valid.
        cachedTextGenerator.Invalidate();

        base.OnRebuildRequested();
    }

    // The Text inspector editor can change the font, and we need a way to track changes so that we get the appropriate rebuild callbacks
    // We can intercept changes in OnValidate, and keep track of the previous font reference
    protected override void OnValidate()
    {
        if (!IsActive())
        {
            base.OnValidate();
            return;
        }

        if (m_FontData.font != m_LastTrackedFont)
        {
            BaseFont newFont = m_Font;
            m_Font = m_LastTrackedFont;

            //if (isActiveAndEnabled)
            //    FontUpdateTracker.UntrackText(this);

            m_Font = newFont;

            //if (isActiveAndEnabled)
            //    FontUpdateTracker.TrackText(this);

            m_LastTrackedFont = newFont;
        }
        base.OnValidate();
    }

#endif // if UNITY_EDITOR

    public static TextGenerationSettingsEx settingsex;

    /// <summary>
    /// 配置文本生成器的参数
    /// </summary>
    /// <param name="size">参考尺寸</param>
    /// <param name="sett">参数载体</param>
    public void GetGenerationSettings(ref Vector2 size, ref TextGenerationSettingsEx sett)
    {
        //var font = Font;
        sett.font = m_Font;
        sett.pivot = rectTransform.pivot;
        sett.generationExtents = size;
        sett.horizontalOverflow = m_FontData.horizontalOverflow;
        sett.verticalOverflow = m_FontData.verticalOverflow;
        sett.resizeTextMaxSize = resizeTextMaxSize;
        sett.resizeTextMinSize = resizeTextMinSize;
        sett.generateOutOfBounds = false;//generateOutOfBounds;
        sett.resizeTextForBestFit = m_FontData.bestFit;
        sett.textAnchor = m_FontData.alignment;
        sett.fontStyle = m_FontData.fontStyle;
        sett.scaleFactor = 1;
        //sett.richText = m_richText;
        sett.lineSpacing = m_FontData.lineSpacing;
        sett.fontSize = m_FontData.fontSize;
        sett.color = color;
        sett.alignByGeometry = m_FontData.alignByGeometry;
    }
}
